﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.IO;
using Microsoft.ComplexEventProcessing;
using Microsoft.ComplexEventProcessing.Adapters;

namespace ProIAdapters.Input.SecurityEventLog
{
    /// <summary>
    /// Input adapter for Security Event Log
    /// 
    /// 
    /// 
    /// </summary>
    public class SecurityLogInput : TypedPointInputAdapter<SecurityLogEvent>
    {
        private readonly static IFormatProvider DateFormatProvider = CultureInfo.GetCultureInfo("en-us").DateTimeFormat;
        private const string DateFormatString = "ddd MMM dd HH:mm:ss yyyy";
        private PointEvent<SecurityLogEvent> pendingEvent;
        private SecurityLogConfig _config;
        const int streamBufferSize =  100000;
        const int dataArraySize = 100000;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="config">Configuration for this adapter</param>
        public SecurityLogInput(SecurityLogConfig config)
        {
            _config = config;
        }

        public override void Start()
        {
            // start background thread to stack events on queue
            ProduceEvents();
        }

        public override void Resume()
        {
            ProduceEvents();
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

        /// <summary>
        /// Main loop
        /// </summary>
        private void ProduceEvents()
        {
            
                var currEvent = default(PointEvent<SecurityLogEvent>);
                string debug = "empty";
                // Loop until stop signal
                while (AdapterState != AdapterState.Stopping)
                {
                    try
                    {
                    System.Threading.Thread.Sleep(_config.Timeout); 
                    Socket clientSocket = new Socket(AddressFamily.InterNetwork,
                    SocketType.Stream, ProtocolType.Tcp);
                    //clientSocket.Connect(new IPEndPoint(
                    //Dns.GetHostAddresses(_config.ServerName)[0], _config.Port));
                    clientSocket.Connect(new IPEndPoint(
                    IPAddress.Parse(_config.ServerIp), _config.Port));
                    Console.WriteLine("Client is connected to " + _config.ServerIp + " " + _config.ServerName + "\n");
                     // Create a NetworkStream that owns clientSocket and
                     // then create a BufferedStream on top of the NetworkStream.
                     // Both streams are disposed when execution exits the
                     // using statement.
                    using (Stream
                         netStream = new NetworkStream(clientSocket, true),
                         bufStream =
                               new BufferedStream(netStream, streamBufferSize))
                    {
                     // Check whether the underlying stream supports seeking.
                     //Console.WriteLine("NetworkStream {0} seeking.\n",
                      //   bufStream.CanSeek ? "supports" : "does not support");
                             if (bufStream.CanRead)
                             {
                                 DateTime startTime;
                                 double networkTime, bufferedTime = 0;
                                 int bytesReceived = 0;
                                 byte[] receivedData = new byte[dataArraySize];
 
                                 Console.WriteLine("Receiving data from " + _config.ServerName);
                                 bytesReceived = 0;
                                 startTime = DateTime.Now;

                                 int numBytesToRead = receivedData.Length;

                                 //while (numBytesToRead > 0)
                                 //{
                                 //    // Read may return anything from 0 to numBytesToRead.
                                 //    int n = bufStream.Read(receivedData, 0, receivedData.Length);
                                 //    // The end of the file is reached.
                                 //    if (n == 0)
                                 //        break;
                                 //    bytesReceived += n;
                                 //    numBytesToRead -= n;
                                 //}

                             }
                             
                             using (var streamReader = new StreamReader(netStream))
                             {
                                     EnqueueCtiEvent(DateTimeOffset.Now);
                                     try
                                     {

                                             if (pendingEvent != null)
                                             {
                                                 currEvent = pendingEvent;
                                                 pendingEvent = null;
                                             }
                                             else
                                             {
                                                 // Read from source
                                                 // streamReader.
                                                 while (!streamReader.EndOfStream)
                                                 {
                                                     var line = streamReader.ReadLine();
                                                     if (line == null)
                                                         continue;
                                                         // Parse
                                                         string[] fields = line.Split('|');
                                                         if (fields.Length < 10)
                                                         continue;
                                                         var logevent = new SecurityLogEvent();
                                                         logevent.RecordID = Convert.ToInt64(fields[0]);
                                                         logevent.TimeCreated = Convert.ToDateTime(fields[1]);
                                                         logevent.ProcessId = Convert.ToInt32(fields[2]);
                                                         logevent.ThreadId = Convert.ToInt32(fields[3]);
                                                         logevent.EventId = Convert.ToInt32(fields[4]);                                                           
                                                         logevent.MachineName = fields[5];
                                                         logevent.TargetUserSid = fields[6];
                                                         //Console.WriteLine(fields[7]);
                                                         int result;
                                                         logevent.TargetUserInt = (Int32.TryParse(fields[7],out result) == true)? result : 99999;
                                                         logevent.TargetUserName = fields[8];
                                                         if (fields.Length == 12)
                                                         {
                                                             logevent.TargetIp = fields[9];
                                                             logevent.LogonType = (Int32.TryParse(fields[10], out result) == true) ? result : 99;
                                                             logevent.Keywords = fields[11];
                                                         }
                                                         else
                                                         {
                                                             logevent.TargetIp = "0.0.0.0";
                                                             logevent.LogonType = 99;
                                                             logevent.Keywords = fields[10];
                                                         }
                                                         // Produce INSERT event
                                                         currEvent = CreateInsertEvent();
                                                         currEvent.StartTime = DateTimeOffset.Now;
                                                         currEvent.Payload = logevent;
                                                         pendingEvent = null;
                                                         
                                                         //PrintEvent(currEvent);
                                                         Enqueue(ref currEvent);
                                                         // Also send a CTI event
                                                         EnqueueCtiEvent(DateTimeOffset.Now);    
                                                 }
                                             
                                         } 
                                         if (pendingEvent != null)
                                         {
                                             currEvent = pendingEvent;
                                             pendingEvent = null;
                                         }



                                    }
                                    catch (Exception e)
                                    {
                                        Console.WriteLine(debug + "\n\r" + this.GetType().ToString() + ".ProduceEvents - " + e.Message + e.StackTrace);
                                    }
 
                                  

                               }// stream reader scope 
                                
  
                                 // When bufStream is closed, netStream is in turn
                                 // closed, which in turn shuts down the connection
                                 // and closes clientSocket.
                                 Console.WriteLine("\nShutting down the connection.");
                                // bufStream.Close();
                                            
                        } // end of bufstream scope
                    } // end of try block
                    catch (System.Net.Sockets.SocketException e)
                    {
                        Console.WriteLine("No server at the other end " + _config.ServerIp + " " + _config.ServerName);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(this.GetType().ToString() + ".ProduceEvents - " + e.Message + e.StackTrace);
                    }  
                } 
                PrepareToStop(currEvent);
                Stopped();
              
            
        
    } //method close

        private void pause()
        {
            System.Threading.Thread.Sleep(10000);
        }

        private string GetLog()
        {
            string result;

            switch (_config.Mode)
            {
                case SecurityLogMode.Sample:
                    //link will be to WCF service
                    result = "";
                    break;
                case SecurityLogMode.Firehose:
                    //link will be to WCF service
                    result = "";
                    break;
                case SecurityLogMode.Filter:
                    //link will be to WCF service
                    result = "scsproi003:55900";
                    break;
                default:
                    throw new Exception("Invalid Config Mode");
            }

            if (!string.IsNullOrEmpty(_config.Parameters))
                result += "?" + _config.Parameters;

            return result;
        }

 
        private void PrepareToStop(PointEvent<SecurityLogEvent> currEvent)
        {
            //EnqueueCtiEvent(DateTime.Now);
            if (currEvent != null)
            {
                // Do this to avoid memory leaks
                ReleaseEvent(ref currEvent);
            }
        }

        private void PrepareToResume(PointEvent<SecurityLogEvent> currEvent)
        {
            pendingEvent = currEvent;
        }

        private string Unquote(string str)
        {
            return str.Trim('"');
        }

        /// <summary>
        /// Debugging function
        /// </summary>
        /// <param name="evt"></param>
        private void PrintEvent(PointEvent<SecurityLogEvent> evt)
        {
        }


    }
}

                                                  // Receive data using the NetworkStream.  TEST LATER
                                 //Console.WriteLine("Receiving data using NetworkStream.");
                                 //startTime = DateTime.Now;
                                 //while (bytesReceived < numberOfLoops * receivedData.Length)
                                 //{
                                 //    bytesReceived += netStream.Read(
                                 //        receivedData, 0, receivedData.Length);
                                 //}
                                 //networkTime = (DateTime.Now - startTime).TotalSeconds;
                                 //Console.WriteLine("{0} bytes received in {1} seconds.\n",
                                 //    bytesReceived.ToString(),
                                 //    networkTime.ToString("F1"));

                                 // Receive data using the BufferedStream.
                                //bufferedTime = (DateTime.Now - startTime).TotalSeconds;
                                 //Console.WriteLine("{0} bytes received in {1} seconds.\n",
                                 //    bytesReceived.ToString(),
                                 //    bufferedTime.ToString("F1"));
                                 // Print the ratio of read times.
                                 //Console.WriteLine("Receiving data using the buffered network" +
                                 //    " stream was {0} {1} than using the network stream alone.",
                                 //    (networkTime / bufferedTime).ToString("P0"),
                                 //    bufferedTime < networkTime ? "faster" : "slower");
